import { File, IFile } from '../models/file'
import {
  DatabaseError,
  NotFoundError,
  ValidationError
} from '../errors/CustomError'
import { isMongoError } from '../errors/MongoError'
import fs from 'fs'
import path from 'path'

class FileService {
  // 处理上传的文件
  async handleUpload(files: any, userId: number | null): Promise<IFile[]> {
    const fileDataList: IFile[] = []

    if (files) {
      for (const fieldName in files) {
        const fileArray = Array.isArray(files[fieldName])
          ? files[fieldName]
          : [files[fieldName]]

        for (const file of fileArray) {
          // 获取文件信息
          const stats = fs.statSync(file.filepath)
          const uniqueFileName = path.basename(file.filepath)

          const fileData: Partial<IFile> = {
            original_name: file.originalFilename,
            file_name: uniqueFileName,
            file_path: file.filepath,
            file_size: stats.size,
            mime_type: file.mimetype,
            created_by: userId
          }

          const savedFile = await this.create(fileData)
          fileDataList.push(savedFile)
        }
      }
    }

    return fileDataList
  }

  // 创建文件记录
  async create(fileData: Partial<IFile>): Promise<IFile> {
    try {
      const file = new File(fileData)
      return await file.save()
    } catch (error) {
      if (isMongoError(error) && error.code === 11000) {
        throw new ValidationError('文件数据重复')
      }

      if (error instanceof Error && error.name === 'ValidationError') {
        throw new ValidationError('文件数据验证失败', (error as any).errors)
      }

      throw new DatabaseError('创建文件记录失败', error)
    }
  }

  // 删除文件记录及物理文件
  async delete(id: number): Promise<boolean> {
    try {
      const file = await File.findOne({ id })

      if (!file) {
        throw new NotFoundError('文件记录未找到')
      }

      // 删除物理文件
      try {
        fs.unlinkSync(file.file_path)
      } catch (error) {
        // 文件可能已经不存在，继续删除记录
        console.warn(`文件 ${file.file_path} 不存在或删除失败`)
      }

      // 删除数据库记录
      const result = await File.findOneAndDelete({ id })

      if (!result) {
        throw new NotFoundError('文件记录未找到')
      }

      return true
    } catch (error) {
      if (error instanceof NotFoundError) {
        throw error
      }
      throw new DatabaseError('删除文件记录失败')
    }
  }

  // 批量删除文件记录及物理文件
  async deleteMultiple(ids: number[]): Promise<boolean> {
    try {
      // 查找所有要删除的文件
      const files = await File.find({ id: { $in: ids } })

      // 删除物理文件
      for (const file of files) {
        try {
          fs.unlinkSync(file.file_path)
        } catch (error) {
          // 文件可能已经不存在，继续删除记录
          console.warn(`文件 ${file.file_path} 不存在或删除失败`)
        }
      }

      // 批量删除数据库记录
      await File.deleteMany({ id: { $in: ids } })

      return true
    } catch (error) {
      throw new DatabaseError('批量删除文件记录失败')
    }
  }

  // 更新文件记录
  async update(id: number, updateData: Partial<IFile>): Promise<IFile | null> {
    try {
      // 移除不能更新的字段
      const { created_at, ...updateFields } = updateData

      const file = await File.findOneAndUpdate({ id }, updateFields, {
        new: true
      })

      if (!file) {
        throw new NotFoundError('文件记录未找到')
      }

      return file
    } catch (error) {
      if (error instanceof NotFoundError || error instanceof ValidationError) {
        throw error
      }
      if (isMongoError(error) && error.code === 11000) {
        throw new ValidationError('文件数据重复')
      }

      if (error instanceof Error && error.name === 'ValidationError') {
        throw new ValidationError('文件数据验证失败', (error as any).errors)
      }
      throw new DatabaseError('更新文件记录失败')
    }
  }

  // 查询所有文件记录
  async findAll(query: any = {}): Promise<IFile[]> {
    try {
      const filter: any = {}

      // 根据原始文件名搜索
      if (query.original_name) {
        filter.original_name = { $regex: query.original_name, $options: 'i' }
      }

      // 根据MIME类型过滤
      if (query.mime_type) {
        filter.mime_type = query.mime_type
      }

      const files = await File.find(filter)
      return files.map((file) => {
        return file.toJSON() as unknown as IFile
      })
    } catch (error) {
      throw new DatabaseError('查询文件列表失败')
    }
  }

  // 查询单个文件记录
  async findOne(id: number): Promise<IFile | null> {
    try {
      const file = await File.findOne({ id })

      if (!file) {
        throw new NotFoundError('文件记录未找到')
      }

      return file
    } catch (error) {
      if (error instanceof NotFoundError) {
        throw error
      }
      throw new DatabaseError('查询文件记录失败')
    }
  }
}

export const fileService = new FileService()
